#include "vtkMeshExport.hpp"
#include "vtkFieldExport.hpp"
#include "vtkFieldExport_boundary.hpp"
#include "vtkFieldExport_constant.hpp"
#include "vtkFieldExport_source.hpp"
#include "fieldFunctions.hpp"

#include "bc_swe_all.hpp"

#include "structMesh_testFunctions.hpp"

#include "vtkFieldImport_constant.hpp"

#include <catch2/catch_test_macros.hpp>

using namespace hms;
using namespace hms::vtk;
namespace xml = tinyxml2;

#ifdef IGNORE

TEST_CASE( "Printing xml output for visualisation", "[pureOutput]" ){
	using MeshType = UniMesh;
	UniMesh mesh { 3, 4, 1, 1, 1 };

	addStandardBoundaryPatches(mesh);

	Time time {10};
	time.writeTimeInterval(1);

	SWESolver<MeshType> solver {mesh, time};

	Field<MeshType>
		& d { solver.d() },
		& v { solver.v() },
		& z { solver.z() };

	v.values().colwise() = Array2s{1, 0};
	d.values() = 1;
	z.values() = -1.0;

	/* create two dry cells */
	d.values()(0, mesh.cell(1,1).index() ) = 0;
	d.values()(0, mesh.cell(1,2).index() ) = 0;


	const BoundaryPatch
		& top { mesh.boundaryPatch("top") },
		& left { mesh.boundaryPatch("left") },
		& right { mesh.boundaryPatch("right") },
		& bottom { mesh.boundaryPatch("bottom") };
	
	/* boundary conditions */
	bool uniform{true}, nonUniform{false};
	
	DynamicValue
		vLeft {
			v, left, time,
			TimeSeries{
				{0, Array2s{1.5, 0} },
				{1, Array2s{0, -1.5} }
			},
			uniform
		},
		dLeft {
			d, left, time,
			TimeSeries{
				{0, Array4s{1,2,3,4}.transpose() },
				{5, Array4s{4,3,2,1}.transpose() }
			},
			nonUniform
		},
		zLeft {
			z, left, time,
			std::vector<FunctionParserWrapper>{
				{"sin(t)"}
			},
			uniform,
			z
		},
		vRight {
			v, right, time,
			std::vector<FunctionParserWrapper>{
				{"cos(t)"},
				{"x+2*y"}
			},
			nonUniform,
			z
		};
		

	v.setBoundaryCondition( StaticValue{v, bottom, Array2s{1,2}, uniform} );
	d.setBoundaryCondition( StaticValue{d, bottom, ArrayNNs<1,3>{0.0, -0.1, 0.1}, nonUniform} );
	z.setBoundaryCondition( ZeroGradient{z, bottom} );

	v.setBoundaryCondition( std::move(vLeft) );
	d.setBoundaryCondition( std::move(dLeft) );
	z.setBoundaryCondition( std::move(zLeft) );

	v.setBoundaryCondition( std::move(vRight) );
	d.setBoundaryCondition( FreeOutflow_waterdepth{d, right, true, 42} );
	z.setBoundaryCondition( ZeroGradient{z, right} );

	v.setBoundaryCondition( OrthogonalFlowVelocity_velocity{v, top, 5} );
	d.setBoundaryCondition( OrthogonalFlowVelocity_waterdepth{d, top, v, 5} );
	z.setBoundaryCondition( ZeroGradient{z, top} );

	/* option 1 */
	SourceField<Uniform, MeshType> rainfall1 { mesh, Array1s{0.3} };

	/* option 2 */
	ArrayXXs rainVals (1, nFieldCols(mesh) );
	rainVals.setRandom();
	SourceField<NonUniform, MeshType> rainfall2 { mesh, std::move(rainVals) };

	/* option 3 */
	TimeSeries rainTimeSeriesU {
		{0, Array1s{1}}, {10, Array1s{2}}
	};
	SourceField<Uniform, MeshType> rainfall3 {
		mesh, std::move(rainTimeSeriesU), time
	};

	/* option 4 */
	ArrayXXs rainVals0 (1, nFieldCols(mesh) ), rainVals1;
	rainVals1.resizeLike(rainVals0);
	rainVals0.setRandom();
	rainVals1.setRandom();
	TimeSeries rainTimeSeriesNU {
		{0, std::move(rainVals0)}, {10, std::move(rainVals1)}
	};
	SourceField<NonUniform, MeshType> rainfall4 {
		mesh, std::move(rainTimeSeriesNU), time, true
	};

	solver.useRainfall(true);
	solver.useInfiltration(true);


	solver.r() = std::move(rainfall1);
	solver.i() = std::move(rainfall2);
	std::unique_ptr<xml::XMLDocument> sourceDoc { sourceFieldsToXML(solver) };
	toStream( sourceDoc.get(), std::cout );

	solver.r() = std::move(rainfall3);
	solver.i() = std::move(rainfall4);
	sourceDoc = sourceFieldsToXML(solver);
	toStream( sourceDoc.get(), std::cout );
	// std::visit( [&](const auto& r){
	// 	std::cout << "Rainfall [m/s]: " << r.values() << "\n";
	// }, solver.r() );

	
	
	// /* option 1 */
	// auto
	// 	meshDoc     { std::make_unique<xml::XMLDocument>() },
	// 	boundaryDoc { std::make_unique<xml::XMLDocument>() },
	// 	constDoc    { std::make_unique<xml::XMLDocument>() },
	// 	sourceDoc   { std::make_unique<xml::XMLDocument>() };
	
	// meshToVtk(meshDoc.get(), mesh, false);
	// boundaryToXML(boundaryDoc.get(), mesh);
	// writeFields( meshDoc.get(), solver );
	// writeBoundaryConditions(boundaryDoc.get(), solver);
	// writeConstantFields( constDoc.get(), solver);
	// writeSourceFields(sourceDoc.get(), solver);

	/* option 2 */
	std::unique_ptr<xml::XMLDocument>
		meshDoc     { meshToVtk(mesh, false) },
	// 	boundaryDoc { boundaryToXML(mesh) },
		constDoc    { constantFieldsToXML(solver) };
	// 	sourceDoc   { sourceFieldsToXML(solver) };
	

	writeFields( meshDoc.get(), solver );
	// writeBoundaryConditions( boundaryDoc.get(), solver );

	xml::XMLElement* constFields {
		getChildNodeByName( constDoc.get(), constNode )
	};
	xml::XMLElement* manning { getChildNodeByAttributeValue(constFields,
		constChild, "Name", "ManningsN"
	) };
	// manning->SetAttribute("isUniform", false);
	// manning->SetAttribute("isFunction", true);
	// writeFunction(manning, "x+y+z", 0);

	manning->SetAttribute("isUniform" , false);
	manning->SetAttribute("isFunction", false);
	manning->SetText("0 1 2 3 4 5 6 7 8 9 10 11");
		

	toStream( meshDoc.get(), std::cout );
	// toStream( boundaryDoc.get(), std::cout );
	// toStream( constDoc.get(), std::cout );

	readConstantFields( constDoc.get(), solver );

	std::visit( [&](const auto& field){
		// std::cout << "ManningsN: " << field.storage() << "\n";
		std::cout << "ManningsN:\n";
		printAsStructMesh( field.domainValues(), mesh.size() );
	}, solver.n() );
	
	// toStream( sourceDoc.get(), std::cout );

	// ArrayXXs arr;
	// std::cout << "Is empty: " << std::boolalpha << (arr.size() == 0) << "\n";

	// auto constPrint = [](const auto& field){
	// 	std::cout << "const types:"
	// 		<< "\n" << type_name<decltype(field.values())>() 
	// 		<< "\n" << type_name<decltype(field.domainValues())>()
	// 		<< "\n";
	// };
	// auto nonConstPrint = [](auto& field){
	// 	std::cout << "Non-const type:"
	// 		<< "\n" << type_name<decltype(field.values())>() 
	// 		<< "\n" << type_name<decltype( field.domainValues() )>()
	// 		<< "\n";
	// };

	// constPrint( solver.z() );
	// nonConstPrint( solver.z() );
	// constPrint( solver.v() );
	// nonConstPrint( solver.v() );
}

#endif